//
// Created by hunan on 25-3-8.
//

#include "tcp_socket_server_connection.h"

#include "socket_address.h"
#include "socket_channel.h"
#include "socket_description.h"
#include "socket_server_connection.h"

#include <arpa/inet.h>
#include <cerrno>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

class TcpSocketServerChannel : public SocketChannel
{
public:
    explicit TcpSocketServerChannel(Loop * loop, const std::shared_ptr<Description> & description, TcpSocketServer * server);
    ~TcpSocketServerChannel() override = default;
    void OnErrorEvent(std::shared_ptr<SocketChannel> channel, std::shared_ptr<Description> description) override;
    void OnReadEvent(std::shared_ptr<SocketChannel> channel, std::shared_ptr<Description> description) override;
    void OnWriteEvent(std::shared_ptr<SocketChannel> channel, std::shared_ptr<Description> description) override;

private:
    TcpSocketServer * m_server;
};

class TcpSocketServerConnectionChannel : public SocketChannel
{
public:
    explicit TcpSocketServerConnectionChannel(Loop * loop, std::shared_ptr<Description> & description,
                                              TcpSocketServerConnection * connection);
    ~TcpSocketServerConnectionChannel() override = default;
    void OnErrorEvent(std::shared_ptr<SocketChannel> channel, std::shared_ptr<Description> description) override;
    void OnReadEvent(std::shared_ptr<SocketChannel> channel, std::shared_ptr<Description> description) override;
    void OnWriteEvent(std::shared_ptr<SocketChannel> channel, std::shared_ptr<Description> description) override;

private:
    TcpSocketServerConnection * m_connection;
};

TcpSocketServerChannel::TcpSocketServerChannel(Loop * loop, const std::shared_ptr<Description> & description,
                                               TcpSocketServer * server)
    : SocketChannel(loop, description)
{
    m_server = server;
}

void TcpSocketServerChannel::OnReadEvent(std::shared_ptr<SocketChannel> channel, std::shared_ptr<Description> description)
{
    printf("description %d tcp server trigger read event.\n", description->DescriptionData());
    while (true)
    {
        struct sockaddr_in sai    = {};
        socklen_t          len    = sizeof(sai);
        int                result = accept(description->DescriptionData(), reinterpret_cast<struct sockaddr *>(&sai), &len);
        if (result < 0)
        {
            if (errno == EAGAIN)
            {
                result = 0;
            }
            break;
        }
        else
        {
            if (m_server->m_on_conn_cb)
            {
                SocketAddress addr               = SocketAddress::Address(sai);
                auto          socket_description = std::make_shared<SocketDescription>(result);
                auto          connection         = std::shared_ptr<SocketServerConnection>(new TcpSocketServerConnection(m_server, socket_description));
                connection->UpdateConnState(SocketServer::EmConnectionConnected);
                connection->UpdatePeerAddress(addr);
                connection->SetOnMessage([this](const std::shared_ptr<SocketServerConnection>& conn, int ret, SocketServer::Message & message) {
                    m_server->m_on_message_cb(conn, ret, message);
                });
                connection->SetOnDisConn([this](const std::shared_ptr<SocketServerConnection>& conn, int ret) {
                    m_server->m_connection_set.erase(conn);
                    if (m_server->m_on_dis_conn_cb)
                    {
                        m_server->m_on_dis_conn_cb(conn, ret);
                    }
                });
                connection->AsyncRead();
                m_server->m_connection_set.insert(connection);
                m_server->m_on_conn_cb(connection);
            }
            if (!m_server->m_option.m_enable_nonblock)
            {
                result = 0;
                break;
            }
        }
    }
    channel->ModEvent(EmIoOneShot | EmIoIn);
}

void TcpSocketServerChannel::OnErrorEvent(std::shared_ptr<SocketChannel>, std::shared_ptr<Description>)
{
}

void TcpSocketServerChannel::OnWriteEvent(std::shared_ptr<SocketChannel>, std::shared_ptr<Description>)
{
}

TcpSocketServerConnectionChannel::TcpSocketServerConnectionChannel(Loop * loop, std::shared_ptr<Description> & description,
                                                                   TcpSocketServerConnection * connection)
    : SocketChannel(loop, description)
{
    m_connection = connection;
}

void TcpSocketServerConnectionChannel::OnErrorEvent(std::shared_ptr<SocketChannel> channel,
                                                    std::shared_ptr<Description>   description)
{
    printf("description %d tcp server connection trigger error event.\n", description->DescriptionData());
    m_connection->m_channel->DelEvent();
    if (m_connection->m_on_dis_conn_cb)
    {
        m_connection->m_on_dis_conn_cb(m_connection->shared_from_this(), -1);
    }
}

void TcpSocketServerConnectionChannel::OnReadEvent(std::shared_ptr<SocketChannel> channel,
                                                   std::shared_ptr<Description>   description)
{
    printf("description %d tcp server connection trigger read event.\n", description->DescriptionData());
    while (true)
    {
        if (m_connection->m_read_buffer.BufferFreeSize() > 65535)
        {
            m_connection->m_read_buffer.ExpendBuffer(65535);
        }
        int result = read(description->DescriptionData(), m_connection->m_read_buffer.BufferEnd(),
                          m_connection->m_read_buffer.BufferFreeSize());
        if (result == 0 || (result < 0 && errno != EAGAIN))
        {
            m_connection->UpdateConnState(SocketServer::EmConnectionDisConnected);
            channel->DelEvent();
            if (m_connection->m_on_dis_conn_cb)
            {
                m_connection->m_on_dis_conn_cb(m_connection->shared_from_this(), -1);
            }
            break;
        }
        else
        {
            if (result > 0)
            {
                m_connection->m_read_buffer.MovePosition(result);
                if (m_connection->m_read_buffer.BufferFreeSize() > 0)
                {
                    break;
                }
            }
            else
            {
                break;
            }
        }
    }
    if (m_connection->ConnectionState() == SocketServer::EmConnectionConnected)
    {
        if (m_connection->m_read_buffer.BufferDataSize() > 0)
        {
            if (m_connection->m_on_message_cb)
            {
                SocketServer::Message msg = {};
                msg.m_data                = m_connection->m_read_buffer.BufferData();
                msg.m_data_length         = m_connection->m_read_buffer.BufferDataSize();
                msg.m_use_length          = 0;
                m_connection->m_on_message_cb(m_connection->shared_from_this(), 0, msg);
                if (msg.m_use_length > 0)
                {
                    m_connection->m_read_buffer.RemoveSize(msg.m_use_length);
                }
            }
            else
            {
                m_connection->m_read_buffer.MovePosition(m_connection->m_read_buffer.BufferDataSize());
            }
        }
        int event = m_connection->m_write_buffer.BufferDataSize() > 0 ? EmIoOut : 0;
        channel->ModEvent(EmIoIn | EmIoOneShot | EmIoRdHup | event);
    }
}

void TcpSocketServerConnectionChannel::OnWriteEvent(std::shared_ptr<SocketChannel> channel,
                                                    std::shared_ptr<Description>   description)
{
    printf("description %d tcp server connection trigger write event.\n", description->DescriptionData());
    if (m_connection->ConnectionState() == SocketServer::EmConnectionConnected)
    {
        if (m_connection->m_write_buffer.BufferDataSize() > 0)
        {
            int result = write(description->DescriptionData(), m_connection->m_write_buffer.BufferData(),
                               m_connection->m_write_buffer.BufferDataSize());
            if (result > 0)
            {
                m_connection->m_write_buffer.RemoveSize(result);
                if (m_connection->m_write_buffer.BufferDataSize() > 0)
                {
                    channel->ModEvent(EmIoIn | EmIoOneShot | EmIoRdHup | EmIoOut);
                }
            }
            if (result < 0 && errno != EAGAIN)
            {
                channel->DelEvent();
            }
            else
            {
                channel->ModEvent(EmIoIn | EmIoOneShot | EmIoRdHup);
            }
        }
    }
}

TcpSocketServer::TcpSocketServer(Loop * loop) : SocketServer(), m_option()
{
    m_loop = loop;
}

int TcpSocketServer::BindAddress(const SocketServer::Option & option)
{
    m_option      = option;
    m_description = std::make_shared<SocketDescription>(SocketDescription::TCPSOCKET);
    int result    = m_description->CreateDescription();
    if (result < 0)
    {
        return -1;
    }
    struct sockaddr_in sai = {};
    sai.sin_family         = AF_INET;
    if (m_option.m_listen_port > 0)
    {
        sai.sin_port = htons(m_option.m_listen_port);
    }
    if (!m_option.m_bind_addr.empty())
    {
        inet_pton(sai.sin_family, m_option.m_bind_addr.c_str(), &sai.sin_addr.s_addr);
    }
    else
    {
        sai.sin_addr.s_addr = INADDR_ANY;
    }
    result = ::bind(m_description->DescriptionData(), reinterpret_cast<struct sockaddr *>(&sai), sizeof(sai));
    if (result < 0)
    {
        return -1;
    }
    m_option = option;
    m_channel = std::make_shared<TcpSocketServerChannel>(m_loop, m_description, this);
    return 0;
}

int TcpSocketServer::Listen()
{
    if (m_description->DescriptionData() > 0)
    {
        auto self = shared_from_this();
        m_channel->Post([self]() {
            listen(self->m_description->DescriptionData(), 1024);
            self->m_channel->AddEvent(EmIoOneShot | EmIoIn);
        });
        return 0;
    }
    return -1;
}

Loop * TcpSocketServer::GetLoop() const
{
    return m_loop;
}

TcpSocketServerConnection::TcpSocketServerConnection(TcpSocketServer *                          server,
                                                     const std::shared_ptr<Description> & description)
{
    m_server      = server;
    m_description = description;
    m_channel     = std::make_shared<TcpSocketServerConnectionChannel>(server->GetLoop(), m_description, this);
}

int TcpSocketServerConnection::AsyncRead()
{
    if (ConnectionState() == SocketServer::EmConnectionConnected)
    {
        auto self = shared_from_this();
        m_channel->Post([self] {
            int event = self->m_write_buffer.BufferDataSize() > 0 ? EmIoOut : 0;
            if (self->m_channel->Index() > 0)
            {
                self->m_channel->ModEvent(event | EmIoIn | EmIoOneShot | EmIoRdHup | EmIoErr);
            }
            else
            {
                self->m_channel->AddEvent(event | EmIoIn | EmIoOneShot | EmIoRdHup | EmIoErr);
            }
        });
        return 0;
    }
    return -1;
}

int TcpSocketServerConnection::AsyncWrite(const uint8_t * data, int data_length)
{
    if (ConnectionState() == SocketServer::EmConnectionConnected)
    {
        m_write_buffer.PushDataToBuffer(data, data_length);
        auto self = shared_from_this();
        m_channel->Post([self] { self->AsyncRead(); });
        return 0;
    }
    return -1;
}

void TcpSocketServerConnection::Close()
{
    if (m_description)
    {
        if (m_channel)
        {
            UpdateConnState(SocketServer::EmConnectionDisConnected);
            auto self = shared_from_this();
            m_channel->Post([self] {
                if (self->m_channel->Index() > 0)
                {
                    self->m_channel->DelEvent();
                }
                self->m_description->CloseDescription();
            });
        }
        else
        {
            UpdateConnState(SocketServer::EmConnectionDisConnected);
        }
    }
}

void TcpSocketServerConnection::UpdateOption(const ClientOption & option)
{

}
